import java.awt.Color;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
import java.util.logging.Logger;

import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

import org.junit.Assert;

public class TextWindow {
	
	private static final int DEFAULT_FONTSIZE = 16;

	private JTextPane jTextf;
	private Logic logicListener;
	private TextPaneWrapper customWrapper;
	private BaseWindow sourceFrame;

	private String lastCommand;

	private int topLine;
	private int botLine;
	
	private Stack<String> prevCommands;
	private Stack<String> nextCommands;

	private ColorGenerator cgen;
	
	/*
	 * Logger for Text Window
	 */
	Logger textWLogger = Logger.getLogger("TextWindowLog.log");

	public TextWindow(boolean type, Logic log, BaseWindow sF) {
		jTextf = new JTextPane();

		cgen = new ColorGenerator();
		
		prevCommands = new Stack<String>();
		nextCommands = new Stack<String>();

		lastCommand = "";

		customWrapper = new TextPaneWrapper();
		jTextf.setEditorKit(customWrapper);

		logicListener = log;

		setTypeOfTextBox(type);
		
		sourceFrame = sF;
	}

	
	/*
	private void loadLog() {

		FileHandler fh;

		try {
			// This block configure the logger with handler and formatter
			fh = new FileHandler("TextWindowLog.log");
			textWLogger.addHandler(fh);
			SimpleFormatter formatter = new SimpleFormatter();
			fh.setFormatter(formatter);

			// the following statement is used to log any messages
			textWLogger.info("Initialize TextWindow Logger");

		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	*/
	
	

	public void prepareCaretInfo() {
		botLine = topLine = getMyCaret().y;
	}

	public void getLowestY() {
		botLine = getMyCaret().y;
	}

	/*
	 * This method changes this TextWindow to what type of Text-box this object
	 * is True indicates its a TextWindow for User input False indicates its a
	 * TextWindow for Display only (To be refactored)
	 */
	private void setTypeOfTextBox(boolean type) {
		jTextf.setEditable(type);
		if (type) {

			AutoCommand nameService = new AutoCommand();

	        // Create the auto completing document model with a reference to the
	        // service and the input field.
	        StyledDocument autoCompleteDocument = (StyledDocument) new AutoCompleteDocument(nameService,
	                jTextf);

	        // Set the auto completing document as the document model on our input
	        // field.
	        jTextf.setDocument(autoCompleteDocument);
			
			jTextf.requestFocusInWindow();

			jTextf.addKeyListener(inputKeyListener());

		}
	}

	private KeyListener inputKeyListener() {
		return new KeyListener() {

			@Override
			public void keyTyped(KeyEvent e) {

			}

			@Override
			public void keyReleased(KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_ENTER) {
					clearAllMsg();
					prepareCaretInfo();
				} else {

					// Get lowest Caret Position

					if (botLine < getMyCaret().y) {
						botLine = getMyCaret().y;
					}
				}
			}

			@Override
			public void keyPressed(KeyEvent e) {

				if (e.getKeyCode() == KeyEvent.VK_ENTER) {
					try {
						// Execute Processing of user input here

						/*
						 * For Logic: Need this to read String fromTheUI =
						 * fromTheUI.replace("\n", "").replace("\r", "");
						 */
						String enteredKey = getInput();

						prevCommands.push(enteredKey);

						String[] userInput = formatUserChoice(enteredKey);

						if (userInput.length > 0) {

							// Clear if command was clear
							if(userInput[0].equals("clear")|| userInput[0].equals("clr")) {

								textWLogger.info("Clear input entered: "
										+ enteredKey);

								// The other End must NOT be null. Command has
								// to go somewhere
								Assert.assertNotNull(sourceFrame);

								sourceFrame.clearDisplay();
							}else if(userInput[0].equals("color") && userInput.length >= 2) {
								sourceFrame.changeColor(userInput[1]);
							}else{
								// The other End must NOT be null. Command has
								// to go somewhere
								Assert.assertNotNull(sourceFrame);
								System.out.println(enteredKey);
								sourceFrame.passResult(logicListener.processUserInput(enteredKey));
							}
						}

						nextCommands.clear();
						// theOtherEnd.addNewText(jTextf.getText());

						clearAllMsg();
					} catch (Exception e1) {
						clearAllMsg();
						sourceFrame.passError(e1.getMessage());
					}
				} else if (e.getKeyCode() == KeyEvent.VK_UP) {

					System.out.println("Top Line is " + topLine);

					System.out.println("My caret is " + getMyCaret().y);

					if (topLine == getMyCaret().y) {
						// System.out.println("Prev command : "+lastCommand);
						// clearAllMsg();
						if (!prevCommands.isEmpty()) {
							lastCommand = prevCommands.pop();
							nextCommands.push(getInput());
							clearAllMsg();
							addUserText(lastCommand);
							getLowestY();
						}
						// else {
						// lastCommand = "";
						// }

					}
				} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
					System.out.println("Top Line is " + botLine);

					System.out.println("My caret is " + getMyCaret().y);

					if (botLine == getMyCaret().y) {
						// System.out.println("Prev command : "+lastCommand);
						// clearAllMsg();
						if (!nextCommands.isEmpty()) {
							lastCommand = nextCommands.pop();
							prevCommands.push(getInput());
							clearAllMsg();
							addUserText(lastCommand);
							getLowestY();
						}
					}
				}
			}
		};
	}

	private String[] formatUserChoice(String userChoice) {
		return userChoice.split(" ", 2);
	}

	private Rectangle getMyCaret() {
		try {
			return jTextf.modelToView(jTextf.getCaretPosition());
		} catch (BadLocationException e) {
			textWLogger.warning("Invalid Location for Input Caret!");
			e.printStackTrace();
		}
		return null;
	}

	private String getInput() {
		String enteredKey = jTextf.getText();
		enteredKey = enteredKey.replace("\n", "").replace("\r", "");

		return enteredKey;
	}

	public JTextPane getTextWindow() {
		return jTextf;
	}

	public boolean setTheFocus() {
		return jTextf.requestFocusInWindow();
	}

	public void addDisplayText(String newMsg) {
		try {
			StyledDocument doc = jTextf.getStyledDocument();
			Style displayStyle = jTextf.addStyle("displayStyle", null);
			StyleConstants.setForeground(displayStyle, Color.BLUE);
			StyleConstants.setFontSize(displayStyle, DEFAULT_FONTSIZE);
			
			
			doc.insertString(doc.getLength(), newMsg, displayStyle);
		} catch (BadLocationException exc) {
			exc.printStackTrace();
		}
	}
	
	public void addAddText(String newMsg) {
		try {
			StyledDocument doc = jTextf.getStyledDocument();
			Style addStyle = jTextf.addStyle("displayStyle", null);
			StyleConstants.setForeground(addStyle, cgen.generateColor("DARK GREEN"));
			StyleConstants.setFontSize(addStyle, DEFAULT_FONTSIZE);
			
			
			doc.insertString(doc.getLength(), newMsg, addStyle);
		} catch (BadLocationException exc) {
			exc.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void addEditText(String newMsg) {
		try {

			StyledDocument doc = jTextf.getStyledDocument();
			Style editStyle = jTextf.addStyle("editStyle", null);
			StyleConstants.setForeground(editStyle, cgen.generateColor("PURPLE"));
			 StyleConstants.setFontSize(editStyle, DEFAULT_FONTSIZE);
			
			
			doc.insertString(doc.getLength(), newMsg, editStyle);
		} catch (BadLocationException exc) {
			exc.printStackTrace();
		}	catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void addDeleteText(String newMsg) {
		try {

			StyledDocument doc = jTextf.getStyledDocument();
			Style deleteStyle = jTextf.addStyle("deleteStyle", null);
			StyleConstants.setForeground(deleteStyle, cgen.generateColor("DARK RED"));
			 StyleConstants.setFontSize(deleteStyle, DEFAULT_FONTSIZE);
			
			
			doc.insertString(doc.getLength(), newMsg, deleteStyle);
		} catch (BadLocationException exc) {
			exc.printStackTrace();
		}	catch (Exception e) {
			e.printStackTrace();
		}
	}
	 
	public void addDefaultText(String newMsg) {
		try {
			StyledDocument doc = jTextf.getStyledDocument();
			Style normStyle = jTextf.addStyle("defaultStyle", null);

			 StyleConstants.setFontSize(normStyle, DEFAULT_FONTSIZE);
			
			
			doc.insertString(doc.getLength(), newMsg, normStyle);
		} catch (BadLocationException exc) {
			exc.printStackTrace();
		}
	}
	
	public void addUserText(String newMsg) {
		try {
			StyledDocument doc = jTextf.getStyledDocument();
			Style userStyle = jTextf.addStyle("userStyle", null);

			
			doc.insertString(doc.getLength(), newMsg, userStyle);
		} catch (BadLocationException exc) {
			exc.printStackTrace();
		}
	}

	public void addUndoText(String newMsg) {
		try {
			StyledDocument doc = jTextf.getStyledDocument();
			Style undoStyle = jTextf.addStyle("undoStyle", null);
			StyleConstants.setForeground(undoStyle, cgen.generateColor("ORANGE").darker());
			 StyleConstants.setFontSize(undoStyle, DEFAULT_FONTSIZE);
			
			
			doc.insertString(doc.getLength(), newMsg, undoStyle);
		} catch (BadLocationException exc) {
			exc.printStackTrace();
		}	catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void addHistoryText(String newMsg) {
		try {
			StyledDocument doc = jTextf.getStyledDocument();
			Style historyStyle = jTextf.addStyle("historyStyle", null);
			StyleConstants.setForeground(historyStyle, Color.magenta.darker());
			 StyleConstants.setFontSize(historyStyle, DEFAULT_FONTSIZE);
			
			
			doc.insertString(doc.getLength(), newMsg, historyStyle);
		} catch (BadLocationException exc) {
			exc.printStackTrace();
		}	catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void addDoneText(String newMsg) {
		try {
			StyledDocument doc = jTextf.getStyledDocument();
			Style historyStyle = jTextf.addStyle("historyStyle", null);
			StyleConstants.setForeground(historyStyle, Color.PINK.darker());
			 StyleConstants.setFontSize(historyStyle, DEFAULT_FONTSIZE);
			
			
			doc.insertString(doc.getLength(), newMsg, historyStyle);
		} catch (BadLocationException exc) {
			exc.printStackTrace();
		}	catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void addNewAnnoucement(String announceThis) {
		try {
			// Append new line
			// Document doc = jTextf.getDocument();

			StyledDocument doc = jTextf.getStyledDocument();

			Style bStyle = jTextf.addStyle("broadCastStyle", null);
			StyleConstants.setForeground(bStyle, Color.DARK_GRAY);
			StyleConstants.setBold(bStyle, true);
			StyleConstants.setFontSize(bStyle, DEFAULT_FONTSIZE);

			doc.insertString(doc.getLength(), announceThis, bStyle);

		} catch (BadLocationException exc) {
			exc.printStackTrace();
		}
	}

	public void addErrorMsg(String errMsg) {
		try {
			StyledDocument doc = jTextf.getStyledDocument();

			Style errStyle = jTextf.addStyle("errorStyle", null);
			StyleConstants.setForeground(errStyle, Color.red);
			StyleConstants.setFontSize(errStyle, DEFAULT_FONTSIZE);

			doc.insertString(doc.getLength(), errMsg, errStyle);
		} catch (BadLocationException exc) {
			exc.printStackTrace();
		}
	}


	public void clearAllMsg() {
		jTextf.setText("");
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
    private static class AutoCommand implements AutoCompleteInterface<String> {
        private List<String> data;

        public AutoCommand() {
            data = Arrays.asList("add", "delete", "edit", "display", "color", "done", "undone", "undo", "history", "redo", "exit", "search");
        }

        @Override
        public String toString() {
            StringBuilder b = new StringBuilder();
            for (String o : data) {
                b.append(o).append("\n");
            }
            return b.toString();
        }

        public String autoCompleteStatement(String startsWith) {
            // Naive implementation, but good enough for the sample
            String hit = null;
            for (String o : data) {
                if (o.startsWith(startsWith)) {
                    // CompletionService contract states that we only
                    // should return completion for unique hits.
                    if (hit == null) {
                        hit = o;
                    } else {
                        hit = null;
                        break;
                    }
                }
            }
            return hit;
        }

    }
	
	
	

}


